<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
  
  
  
  
  

  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge" />
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
  <meta name=generator content="Zola v0.19.2">

  
    <meta name="description" content="A playable online game. Not every possible starting position is solvable, so we have to take special care.">
  

  <title>Zolarwind</title>
  <link rel="stylesheet" href="/css/generated.css">
  
    <link rel="icon" href="&#x2F;img&#x2F;yin-yang.svg" />
  

  
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
    <link rel="alternate" type="application/atom+xml" title="Atom Feed for https://atomgit.com/deng-2022/games-zola/" href="/atom.xml" />
=======
    <link rel="alternate" type="application/atom+xml" title="Atom Feed for https://atomgit.com/deng-2022/games-zola" href="/atom.xml" />
>>>>>>> 88a22dfe7c2ae7ea9585d788af01d772a42c77e8
=======
    <link rel="alternate" type="application/atom+xml" title="Atom Feed for https://deng-2022.atomgit.com/games-zola" href="/atom.xml" />
>>>>>>> ac12026 (third commit)
=======
    <link rel="alternate" type="application/atom+xml" title="Atom Feed for https://deng-2022.atomgit.net/games-zola" href="/atom.xml" />
>>>>>>> b45cb8d (four commit)
  

  <script defer src="/js/zolarwind/logic.js" integrity="sha384-GYtsrgYVRYoxdEL+C+79KROm+MCl1ArSzQnqVnPTV78UNz5a1vFxAEiKbobXhQlw"></script>
  
  
</head>
<body>
  

  


<header class="sticky top-0 w-full z-10">
  <nav class="bg-gray-600">
    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
      <div class="flex items-center justify-between h-16">
        <div class="flex items-center">
          <div class="flex-shrink-0">
            <a href="/" aria-label="Go to homepage">
              <div class="h-8 w-8 text-gray-200 hover:text-white fill-current">
                <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor">
                  <path d="M3 12L5 10M5 10L12 3L19 10M5 10V20C5 20.5523 5.44772 21 6 21H9M19 10L21 12M19 10V20C19 20.5523 18.5523 21 18 21H15M9 21C9.55228 21 10 20.5523 10 20V16C10 15.4477 10.4477 15 11 15H13C13.5523 15 14 15.4477 14 16V20C14 20.5523 14.4477 21 15 21M9 21H15" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
              </div>
            </a>
          </div>
          <div class="hidden md:block">
            <div class="ml-8 flex items-baseline">
              
                
                  
                    <a class="px-3 py-2 ml-2 rounded-md font-medium text-gray-200 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-600" href="/pages/imprint/" title="Imprint">Imprint</a>
                  
                
                  
                    <a class="px-3 py-2 ml-2 rounded-md font-medium text-gray-200 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-600" href="/pages/about/" title="About">About</a>
                  
                
              
            </div>
          </div>
        </div>
        <div class="-mr-2 flex md:hidden">
          <button id="toggleMobileMenu" class="icon inline-flex items-center justify-center p-2 rounded-md text-gray-200 hover:text-white hover:bg-gray-700 focus:outline-none focus:bg-gray-700 focus:text-white" aria-label="Menu">
            <span id="mobile-icon-menu-unselected">
              <svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
              </svg>
            </span>
            <span id="mobile-icon-menu-selected" class="hidden">
              <svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
              </svg>
            </span>
          </button>
        </div>
      </div>
    </div>
    
      <div id="mobile-menu" class="hidden md:hidden px-2 pt-2 pb-3 sm:px-3">
        
          
            <a class="mt-1 block px-3 py-2 rounded-md font-medium text-gray-200 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-600" href="/pages/imprint/" title="Imprint">Imprint</a>
          
        
          
            <a class="mt-1 block px-3 py-2 rounded-md font-medium text-gray-200 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-600" href="/pages/about/" title="About">About</a>
          
        
      </div>
    
  </nav>
</header>

  <div id="content">
    

  

  <div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8">
    <article class="prose">
      <div class="h-48 rounded-md shadow-md">
        <img class="h-48 w-full object-cover" src="banner.jpg" alt="The Game of Fifteen">
      </div>
      <h1>The Game of Fifteen</h1>
      <div>
        <p>
          <span class="mr-2 text-sm">
            <time datetime="2020-06-11">June 11, 2020</time> &middot;
            986 words &middot;
            5 minutes reading time
          </span>
          <span class="text-sm leading-5 font-medium text-indigo-600">

  
    
<<<<<<< HEAD
<<<<<<< HEAD
      <span class="mr-2 badge badge-green"><a href="https://atomgit.com/deng-2022/games-zola/tags/javascript/">javascript</a></span>
=======
      <span class="mr-2 badge badge-green"><a href="https://deng-2022.atomgit.com/games-zola/tags/javascript/">javascript</a></span>
>>>>>>> ac12026 (third commit)
=======
      <span class="mr-2 badge badge-green"><a href="https://deng-2022.atomgit.net/games-zola/tags/javascript/">javascript</a></span>
>>>>>>> b45cb8d (four commit)
    
  

</span>

        </p>
      </div>

      <div>
        <p>The Game of Fifteen is a sliding puzzle that consists of a square with tiles numbered from 1 to 15 in random order with one tile missing.
To solve the puzzle, you must place the tiles in order by moving tiles to the empty space.</p>
<h2 id="how-to-play">How to play</h2>
<p>Play the game online by clicking/tapping on the tile you want to move.
Only tiles neighboring the empty space can be moved.
Once you solve the puzzle, the tiles will not move anymore.
Just play a new game by clicking the button.
I can guarantee you that every game presented here is solvable.
Read below for an explanation.</p>
<p>Hint: if your browser doesn't display the board correctly, try clearing your browser cache and try again.</p>
<div id="board">
    <div class="m-4 grid grid-flow-row grid-cols-4 grid-rows-4 h-64 w-64 gap-1">
        <div id="f0"  class=""></div>
        <div id="f1"  class=""></div>
        <div id="f2"  class=""></div>
        <div id="f3"  class=""></div>
        <div id="f4"  class=""></div>
        <div id="f5"  class=""></div>
        <div id="f6"  class=""></div>
        <div id="f7"  class=""></div>
        <div id="f8"  class=""></div>
        <div id="f9"  class=""></div>
        <div id="f10" class=""></div>
        <div id="f11" class=""></div>
        <div id="f12" class=""></div>
        <div id="f13" class=""></div>
        <div id="f14" class=""></div>
        <div id="f15" class=""></div>
    </div>
</div>
<div class="mt-4">
    <button id="playButton" class="py-2 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-blue-600 shadow-sm hover:bg-blue-500 focus:outline-none focus:shadow-outline active:bg-blue-600 transition duration-150 ease-in-out" onclick="play()" >
        new game
    </button>
</div>
<h2 id="parity-of-a-permutation">Parity of a permutation</h2>
<p>For the Game of Fifteen, a permutation of the numbers 1 to 15 is a state of the game if read line by line - like a book.
Some smart people have figured out, that an even parity (is dividable by 2, like 42) is an invariant of a solvable permutation of the Game of Fifteen.
In computer science, an invariant is a condition that is always true, i.e. doesn't change for a section of code.</p>
<p>The parity of a permutation is simply the number of inversions.
An inversion happens when an element with a lower index has a higher value than a value at a higher index.</p>
<p>Examples:</p>
<ul>
<li><code>1, 2, 3</code> has no inversions, all elements are in order</li>
<li><code>2, 3, 1</code> has two inversions, because <code>2</code> is greater than <code>1</code> and <code>3</code> is greater than <code>1</code></li>
<li><code>1, 3, 2</code> has only one inversion, <code>3</code> is greater than <code>2</code></li>
</ul>
<p>There are about 1.3 Trillion (Billion for us people in Central Europe) possible permutations of the numbers 1 to 15.
Only half of them have an even parity.
When we calculate a random permutation for the start of a game, it then makes sense to filter out all the permutations with an odd parity.
Because we do not want to present an unsolvable game.</p>
<p>Here is a fragment of source code that calculates the parity and checks if it's even for the examples from above.</p>
<pre data-lang="js" style="background-color:#191919;color:#f8f8f2;" class="language-js "><code class="language-js" data-lang="js"><span style="color:#6d6d6d;">// for (let i = 0; i &lt; limit; i += 1) {...} vs
</span><span style="color:#6d6d6d;">// range0(limit).forEach((i) =&gt; {...})
</span><span style="font-style:italic;color:#fbdfb5;">const </span><span style="color:#8cdaff;">range0 </span><span style="color:#ff5e5e;">= </span><span style="color:#ffffff;">(</span><span style="font-style:italic;color:#fc9354;">limit</span><span style="color:#ffffff;">) </span><span style="font-style:italic;color:#fbdfb5;">=&gt; </span><span>[</span><span style="color:#ff5e5e;">...</span><span style="color:#fbe3bf;">Array</span><span>(</span><span style="color:#e9fdac;">limit</span><span>).</span><span style="color:#6699cc;">keys</span><span>()];
</span><span>
</span><span style="font-style:italic;color:#fbdfb5;">const </span><span style="color:#8cdaff;">isEvenPermutation </span><span style="color:#ff5e5e;">= </span><span style="color:#ffffff;">(</span><span style="font-style:italic;color:#fc9354;">p</span><span style="color:#ffffff;">) </span><span style="font-style:italic;color:#fbdfb5;">=&gt; </span><span style="color:#8cdaff;">parity</span><span>(</span><span style="color:#e9fdac;">p</span><span>) </span><span style="color:#ff5e5e;">% </span><span style="color:#fdb082;">2 </span><span style="color:#ff5e5e;">=== </span><span style="color:#fdb082;">0</span><span>;
</span><span>
</span><span style="font-style:italic;color:#fbdfb5;">const </span><span style="color:#8cdaff;">parity </span><span style="color:#ff5e5e;">= </span><span style="color:#ffffff;">(</span><span style="font-style:italic;color:#fc9354;">p</span><span style="color:#ffffff;">) </span><span style="font-style:italic;color:#fbdfb5;">=&gt; </span><span style="color:#8cdaff;">range0</span><span>(</span><span style="color:#e9fdac;">p</span><span>.length)
</span><span>  .</span><span style="color:#8cdaff;">map</span><span>(</span><span style="color:#ffffff;">(</span><span style="font-style:italic;color:#fc9354;">i</span><span style="color:#ffffff;">) </span><span style="font-style:italic;color:#fbdfb5;">=&gt; </span><span style="color:#8cdaff;">range0</span><span>(</span><span style="color:#e9fdac;">p</span><span>.length)
</span><span>    .</span><span style="color:#8cdaff;">filter</span><span>(</span><span style="color:#ffffff;">(</span><span style="font-style:italic;color:#fc9354;">j</span><span style="color:#ffffff;">) </span><span style="font-style:italic;color:#fbdfb5;">=&gt; </span><span style="color:#e9fdac;">i </span><span style="color:#ff5e5e;">&lt; </span><span style="color:#e9fdac;">j </span><span style="color:#ff5e5e;">&amp;&amp; </span><span style="color:#e9fdac;">p</span><span>[</span><span style="color:#e9fdac;">i</span><span>] </span><span style="color:#ff5e5e;">&gt; </span><span style="color:#e9fdac;">p</span><span>[</span><span style="color:#e9fdac;">j</span><span>])
</span><span>    .length
</span><span>  )
</span><span>  .</span><span style="color:#8cdaff;">reduce</span><span>(</span><span style="color:#ffffff;">(</span><span style="font-style:italic;color:#fc9354;">agg</span><span>, </span><span style="font-style:italic;color:#fc9354;">v</span><span style="color:#ffffff;">) </span><span style="font-style:italic;color:#fbdfb5;">=&gt; </span><span style="color:#e9fdac;">agg </span><span style="color:#ff5e5e;">+ </span><span style="color:#e9fdac;">v</span><span>, </span><span style="color:#fdb082;">0</span><span>);
</span><span>
</span><span style="color:#6d6d6d;">// true, 0 inversions
</span><span style="color:#fbe3bf;">console</span><span>.</span><span style="color:#6699cc;">log</span><span>(</span><span style="color:#8cdaff;">isEvenPermutation</span><span>([</span><span style="color:#fdb082;">1</span><span>, </span><span style="color:#fdb082;">2</span><span>, </span><span style="color:#fdb082;">3</span><span>]));
</span><span style="color:#6d6d6d;">// true, 2 inversions
</span><span style="color:#fbe3bf;">console</span><span>.</span><span style="color:#6699cc;">log</span><span>(</span><span style="color:#8cdaff;">isEvenPermutation</span><span>([</span><span style="color:#fdb082;">2</span><span>, </span><span style="color:#fdb082;">3</span><span>, </span><span style="color:#fdb082;">1</span><span>]));
</span><span style="color:#6d6d6d;">// false, 1 inversion
</span><span style="color:#fbe3bf;">console</span><span>.</span><span style="color:#6699cc;">log</span><span>(</span><span style="color:#8cdaff;">isEvenPermutation</span><span>([</span><span style="color:#fdb082;">1</span><span>, </span><span style="color:#fdb082;">3</span><span>, </span><span style="color:#fdb082;">2</span><span>]));
</span></code></pre>
<h2 id="source-code">Source code</h2>
<p>I have created this <a href="https://gist.github.com/thomasweitzel/3fd3197616f0299a2935972edd68bd4a">GitHub Gist</a> with the complete HTML page including the JavaScript source code.
It runs in the browser and uses just <a href="https://tailwindcss.com">Tailwind CSS</a> for styling and plain ES6-style JavaScript.
The source code can be used for all square board sizes, not only 4 by 4.
You have to adjust the boards GUI or dynamically generate it - it's easy.</p>
<script>
  const size = 16;
  const boardSize = 4;
  const tileClasses = 'p-2 h-full w-full text-center align-middle text-3xl leading-normal text-red-900 font-bold bg-white border rounded shadow cursor-pointer select-none';
  const winClasses = 'mt-4 bg-yellow-300 inline-block border border-gray-500 rounded-lg shadow-lg';
  const playClasses = 'mt-4 bg-blue-300 inline-block border border-gray-500 rounded-lg shadow-lg';
  const boardDiv = document.getElementById('board');
  const emptyClasses = '';
  const Direction = {
    RIGHT: 1,
    UP: 2,
    LEFT: 3,
    DOWN: 4,
  };
  const range0 = (limit) => [...Array(limit).keys()];
  const init = () => {
    range0(size).forEach((i) => document.getElementById(`f${i}`).addEventListener("click", move(i)));
  };
  const move = (id) => (_) => {
    if (!hasWon()) {
      const emptyIndex = getEmptyIndex();
      swapIfPossible(id, emptyIndex, Direction.UP);
      swapIfPossible(id, emptyIndex, Direction.LEFT);
      swapIfPossible(id, emptyIndex, Direction.DOWN);
      swapIfPossible(id, emptyIndex, Direction.RIGHT);
      drawElement(id);
      drawElement(emptyIndex);
      drawBoard();
    }
  };
  const swapIfPossible = (id, emptyId, direction) => {
    const emptyCoords = indexToCoords(emptyId);
    const neighborCoords = getNeighbor(direction, emptyCoords);
    if (neighborCoords !== null && coordsToIndex(neighborCoords) === id) {
      const temp = permutation[emptyId];
      permutation[emptyId] = permutation[id];
      permutation[id] = temp;
    }
  };
  const drawElement = (id) => {
    const element = document.getElementById(`f${id}`);
    if (permutation[id] === size - 1) {
      element.innerHTML = '';
      element.className = emptyClasses;
    } else {
      element.innerHTML = `${permutation[id] + 1}`;
      element.className = tileClasses;
    }
  };
  const hasWon = () => range0(size).every((i) => permutation[i] === i);
  const drawBoard = () => {
    if (hasWon()) {
      boardDiv.className = winClasses;
    } else {
      boardDiv.className = playClasses;
    }
  };
  const draw = () => {
    drawBoard();
    range0(size).forEach((i) => drawElement(i));
  };
  const getRandomPermutation = () => {
    const permutation = [];
    const set = new Set(range0(size - 1));
    while (set.size !== 0) {
      const randomIndex = Math.floor(Math.random() * Math.floor(set.size));
      const element = [...set][randomIndex];
      permutation.push(element);
      set.delete(element);
    }
    permutation.push(size - 1);
    return permutation;
  };
  const parity = (p) => range0(size)
    .map((i) => range0(size)
      .filter((j) => i < j && p[i] > p[j])
      .length
    )
    .reduce((agg, v) => agg + v, 0);
  const isEvenPermutation = (p) => parity(p) % 2 === 0;
  const getEvenPermutation = () => {
    let p;
    do {
      p = getRandomPermutation();
    } while (!isEvenPermutation(p))
    return p;
  };
  const indexToCoords = (i) => ({ row: Math.trunc(i / boardSize), column: i % boardSize });
  const coordsToIndex = (coords) => coords.row * boardSize + coords.column;
  const getCellOrNull = (coords) => {
    if (coords.row < 0 || coords.row >= boardSize || coords.column < 0 || coords.column >= boardSize) {
      return null;
    }
    return coords;
  };
  const getNeighbor = (direction, coords) => {
    let result;
    switch (direction) {
      case Direction.RIGHT:
        result = getCellOrNull({ row: coords.row - 1, column: coords.column });
        break;
      case Direction.UP:
        result = getCellOrNull({ row: coords.row + 1, column: coords.column });
        break;
      case Direction.LEFT:
        result = getCellOrNull({ row: coords.row, column: coords.column + 1 });
        break;
      case Direction.DOWN:
        result = getCellOrNull({ row: coords.row, column: coords.column - 1 });
        break;
    }
    return result;
  };
  const getEmptyIndex = () => range0(size).filter((i) => permutation[i] === size - 1)[0];
  const play = () => {
    permutation = getEvenPermutation();
    draw();
  }
  // Lets play ...
  let permutation;
  init();
  play();
</script>

      </div>
    </article>
  </div>

  <script type="application/ld+json">
{
   "@context": "https://schema.org"
  ,"@type": "BlogPosting"
  ,"mainEntityOfPage": {
     "@type": "WebPage"
<<<<<<< HEAD
<<<<<<< HEAD
    ,"@id": "https://atomgit.com/deng-2022/games-zola/blog/game-of-fifteen/"
=======
    ,"@id": "https://deng-2022.atomgit.com/games-zola/blog/game-of-fifteen/"
>>>>>>> ac12026 (third commit)
=======
    ,"@id": "https://deng-2022.atomgit.net/games-zola/blog/game-of-fifteen/"
>>>>>>> b45cb8d (four commit)
  }
  ,"headline": "The Game of Fifteen"
  ,"datePublished": "2020-06-11"
  ,"dateModified": "2020-06-11"
  
  ,"author": [
    
      {
         "@type": "Person"
        ,"name": "Thomas Weitzel"
      }
      
    
  ]
  ,"publisher": [
    
      {
         "@type": "Person"
        ,"name": "Thomas Weitzel"
      }
      
    
  ]
  
  ,"description": "A playable online game. Not every possible starting position is solvable, so we have to take special care."
  ,"articleBody": "The Game of Fifteen is a sliding puzzle that consists of a square with tiles numbered from 1 to 15 in random order with one tile missing.To solve the puzzle, you must place the tiles in order by moving tiles to the empty space.How to playPlay the game online by clicking&#x2F;tapping on the tile you want to move.Only tiles neighboring the empty space can be moved.Once you solve the puzzle, the tiles will not move anymore.Just play a new game by clicking the button.I can guarantee you that every game presented here is solvable.Read below for an explanation.Hint: if your browser doesn&#x27;t display the board correctly, try clearing your browser cache and try again.                                                                                                                                                    new game    Parity of a permutationFor the Game of Fifteen, a permutation of the numbers 1 to 15 is a state of the game if read line by line - like a book.Some smart people have figured out, that an even parity (is dividable by 2, like 42) is an invariant of a solvable permutation of the Game of Fifteen.In computer science, an invariant is a condition that is always true, i.e. doesn&#x27;t change for a section of code.The parity of a permutation is simply the number of inversions.An inversion happens when an element with a lower index has a higher value than a value at a higher index.Examples:1, 2, 3 has no inversions, all elements are in order2, 3, 1 has two inversions, because 2 is greater than 1 and 3 is greater than 11, 3, 2 has only one inversion, 3 is greater than 2There are about 1.3 Trillion (Billion for us people in Central Europe) possible permutations of the numbers 1 to 15.Only half of them have an even parity.When we calculate a random permutation for the start of a game, it then makes sense to filter out all the permutations with an odd parity.Because we do not want to present an unsolvable game.Here is a fragment of source code that calculates the parity and checks if it&#x27;s even for the examples from above.&#x2F;&#x2F; for (let i = 0; i &amp;lt; limit; i += 1) {...} vs&#x2F;&#x2F; range0(limit).forEach((i) =&amp;gt; {...})const range0 = (limit) =&amp;gt; [...Array(limit).keys()];const isEvenPermutation = (p) =&amp;gt; parity(p) % 2 === 0;const parity = (p) =&amp;gt; range0(p.length)  .map((i) =&amp;gt; range0(p.length)    .filter((j) =&amp;gt; i &amp;lt; j &amp;amp;&amp;amp; p[i] &amp;gt; p[j])    .length  )  .reduce((agg, v) =&amp;gt; agg + v, 0);&#x2F;&#x2F; true, 0 inversionsconsole.log(isEvenPermutation([1, 2, 3]));&#x2F;&#x2F; true, 2 inversionsconsole.log(isEvenPermutation([2, 3, 1]));&#x2F;&#x2F; false, 1 inversionconsole.log(isEvenPermutation([1, 3, 2]));Source codeI have created this GitHub Gist with the complete HTML page including the JavaScript source code.It runs in the browser and uses just Tailwind CSS for styling and plain ES6-style JavaScript.The source code can be used for all square board sizes, not only 4 by 4.You have to adjust the boards GUI or dynamically generate it - it&#x27;s easy.  const size = 16;  const boardSize = 4;  const tileClasses = &#x27;p-2 h-full w-full text-center align-middle text-3xl leading-normal text-red-900 font-bold bg-white border rounded shadow cursor-pointer select-none&#x27;;  const winClasses = &#x27;mt-4 bg-yellow-300 inline-block border border-gray-500 rounded-lg shadow-lg&#x27;;  const playClasses = &#x27;mt-4 bg-blue-300 inline-block border border-gray-500 rounded-lg shadow-lg&#x27;;  const boardDiv = document.getElementById(&#x27;board&#x27;);  const emptyClasses = &#x27;&#x27;;  const Direction = {    RIGHT: 1,    UP: 2,    LEFT: 3,    DOWN: 4,  };  const range0 = (limit) =&gt; [...Array(limit).keys()];  const init = () =&gt; {    range0(size).forEach((i) =&gt; document.getElementById(`f${i}`).addEventListener(&#x27;click&#x27;, move(i)));  };  const move = (id) =&gt; (_) =&gt; {    if (!hasWon()) {      const emptyIndex = getEmptyIndex();      swapIfPossible(id, emptyIndex, Direction.UP);      swapIfPossible(id, emptyIndex, Direction.LEFT);      swapIfPossible(id, emptyIndex, Direction.DOWN);      swapIfPossible(id, emptyIndex, Direction.RIGHT);      drawElement(id);      drawElement(emptyIndex);      drawBoard();    }  };  const swapIfPossible = (id, emptyId, direction) =&gt; {    const emptyCoords = indexToCoords(emptyId);    const neighborCoords = getNeighbor(direction, emptyCoords);    if (neighborCoords !== null &amp;&amp; coordsToIndex(neighborCoords) === id) {      const temp = permutation[emptyId];      permutation[emptyId] = permutation[id];      permutation[id] = temp;    }  };  const drawElement = (id) =&gt; {    const element = document.getElementById(`f${id}`);    if (permutation[id] === size - 1) {      element.innerHTML = &#x27;&#x27;;      element.className = emptyClasses;    } else {      element.innerHTML = `${permutation[id] + 1}`;      element.className = tileClasses;    }  };  const hasWon = () =&gt; range0(size).every((i) =&gt; permutation[i] === i);  const drawBoard = () =&gt; {    if (hasWon()) {      boardDiv.className = winClasses;    } else {      boardDiv.className = playClasses;    }  };  const draw = () =&gt; {    drawBoard();    range0(size).forEach((i) =&gt; drawElement(i));  };  const getRandomPermutation = () =&gt; {    const permutation = [];    const set = new Set(range0(size - 1));    while (set.size !== 0) {      const randomIndex = Math.floor(Math.random() * Math.floor(set.size));      const element = [...set][randomIndex];      permutation.push(element);      set.delete(element);    }    permutation.push(size - 1);    return permutation;  };  const parity = (p) =&gt; range0(size)    .map((i) =&gt; range0(size)      .filter((j) =&gt; i  p[j])      .length    )    .reduce((agg, v) =&gt; agg + v, 0);  const isEvenPermutation = (p) =&gt; parity(p) % 2 === 0;  const getEvenPermutation = () =&gt; {    let p;    do {      p = getRandomPermutation();    } while (!isEvenPermutation(p))    return p;  };  const indexToCoords = (i) =&gt; ({ row: Math.trunc(i &#x2F; boardSize), column: i % boardSize });  const coordsToIndex = (coords) =&gt; coords.row * boardSize + coords.column;  const getCellOrNull = (coords) =&gt; {    if (coords.row = boardSize || coords.column = boardSize) {      return null;    }    return coords;  };  const getNeighbor = (direction, coords) =&gt; {    let result;    switch (direction) {      case Direction.RIGHT:        result = getCellOrNull({ row: coords.row - 1, column: coords.column });        break;      case Direction.UP:        result = getCellOrNull({ row: coords.row + 1, column: coords.column });        break;      case Direction.LEFT:        result = getCellOrNull({ row: coords.row, column: coords.column + 1 });        break;      case Direction.DOWN:        result = getCellOrNull({ row: coords.row, column: coords.column - 1 });        break;    }    return result;  };  const getEmptyIndex = () =&gt; range0(size).filter((i) =&gt; permutation[i] === size - 1)[0];  const play = () =&gt; {    permutation = getEvenPermutation();    draw();  }  &#x2F;&#x2F; Lets play ...  let permutation;  init();  play();"
  
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
    ,"image": "https://atomgit.com/deng-2022/games-zola//blog/game-of-fifteen/banner.jpg"
=======
    ,"image": "https://atomgit.com/deng-2022/games-zola/blog/game-of-fifteen/banner.jpg"
>>>>>>> 88a22dfe7c2ae7ea9585d788af01d772a42c77e8
=======
    ,"image": "https://deng-2022.atomgit.com/games-zola/blog/game-of-fifteen/banner.jpg"
>>>>>>> ac12026 (third commit)
=======
    ,"image": "https://deng-2022.atomgit.net/games-zola/blog/game-of-fifteen/banner.jpg"
>>>>>>> b45cb8d (four commit)
  
}
</script>



  </div>
  <footer>
  <!-- Social Media Icons -->
  
    <ul class="mt-4 flex justify-center space-x-8">
      
        
          <li>
            <a
              
              href="https:&#x2F;&#x2F;github.com&#x2F;thomasweitzel" title="Github"
              class="text-gray-400 hover:text-gray-500">
              <span class="sr-only">github</span>
              <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 1024 1024"><path d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" transform="scale(64)"/></svg>
            </a>
          </li>
        
      
        
          <li>
            <a
              
              href="https:&#x2F;&#x2F;stackoverflow.com&#x2F;u&#x2F;188291" title="Stackoverflow"
              class="text-gray-400 hover:text-gray-500">
              <span class="sr-only">stackoverflow</span>
              <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 73.099998 86.599998"><g transform="translate(-22.6,-14.9)"><path d="m 84.4,93.8 0,-23.2 7.7,0 0,30.9 -69.5,0 0,-30.9 7.7,0 0,23.2 z"/><path d="m 38.8,68.4 37.8,7.9 1.6,-7.6 -37.8,-7.9 -1.6,7.6 z m 5,-18 35,16.3 3.2,-7 -35,-16.4 -3.2,7.1 z M 53.5,33.2 83.2,57.9 88.1,52 58.4,27.3 53.5,33.2 Z m 19.2,-18.3 -6.2,4.6 23,31 6.2,-4.6 -23,-31 z M 38,86 l 38.6,0 0,-7.7 -38.6,0 0,7.7 z"/></g></svg>
            </a>
          </li>
        
      
        
          <li>
            <a
              
                rel="me"
              
              href="https:&#x2F;&#x2F;fosstodon.org&#x2F;@tweitzel" title="Mastodon"
              class="text-gray-400 hover:text-gray-500">
              <span class="sr-only">mastodon</span>
              <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 18.656 20"><path d="M18.656 6.566c0-4.339-2.843-5.61-2.843-5.61C14.38.298 11.919.021 9.362 0h-.063c-2.557.021-5.016.298-6.45.956 0 0-2.843 1.272-2.843 5.61 0 .993-.02 2.181.012 3.441.103 4.243.778 8.425 4.7 9.463 1.81.479 3.363.579 4.613.51 2.268-.126 3.54-.809 3.54-.809l-.074-1.646s-1.621.511-3.441.449c-1.804-.062-3.707-.194-4-2.409a4.523 4.523 0 0 1-.04-.621s1.77.433 4.015.536c1.372.063 2.658-.08 3.965-.236 2.506-.299 4.688-1.843 4.962-3.254.434-2.223.398-5.424.398-5.424zm-3.353 5.59h-2.081V7.057c0-1.075-.452-1.62-1.357-1.62-1 0-1.501.647-1.501 1.927v2.791h-2.07V7.364c0-1.28-.5-1.927-1.501-1.927-.905 0-1.357.546-1.357 1.62v5.099H3.355V6.903c0-1.074.273-1.927.823-2.558.566-.631 1.307-.955 2.228-.955 1.065 0 1.872.409 2.405 1.228l.518.869.519-.869c.533-.819 1.34-1.228 2.405-1.228.92 0 1.662.324 2.228.955.549.631.822 1.484.822 2.558z"/></svg>
            </a>
          </li>
        
      
    </ul>
  
  <div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8 text-center">
    
      <p class="text-center text-base leading-6">
        
          
          <a href="&#x2F;pages&#x2F;privacy&#x2F;">Privacy Policy</a>
        
           &middot; 
          <a href="&#x2F;pages&#x2F;terms&#x2F;">Terms</a>
        
      </p>
    
    
    <div class="mt-4">
      <p class="text-center text-base leading-6 text-gray-400">
        Copyright 2025 by Thomas Weitzel
      </p>
    </div>
    
  </div>
</footer>

</body>
</html>
